/***********************************************************************************************************************
*  OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC.
*  See also https://openstudio.net/license
***********************************************************************************************************************/

#include "CurveFanPressureRise.hpp"
#include "CurveFanPressureRise_Impl.hpp"

#include <utilities/idd/OS_Curve_FanPressureRise_FieldEnums.hxx>
#include <utilities/idd/IddEnums.hxx>

#include "../utilities/core/Assert.hpp"

#include <cmath>

using namespace std;

namespace openstudio {
namespace model {

  namespace detail {

    CurveFanPressureRise_Impl::CurveFanPressureRise_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle)
      : Curve_Impl(idfObject, model, keepHandle) {
      OS_ASSERT(idfObject.iddObject().type() == CurveFanPressureRise::iddObjectType());
    }

    CurveFanPressureRise_Impl::CurveFanPressureRise_Impl(const openstudio::detail::WorkspaceObject_Impl& other, Model_Impl* model, bool keepHandle)
      : Curve_Impl(other, model, keepHandle) {
      OS_ASSERT(other.iddObject().type() == CurveFanPressureRise::iddObjectType());
    }

    CurveFanPressureRise_Impl::CurveFanPressureRise_Impl(const CurveFanPressureRise_Impl& other, Model_Impl* model, bool keepHandle)
      : Curve_Impl(other, model, keepHandle) {}

    const std::vector<std::string>& CurveFanPressureRise_Impl::outputVariableNames() const {
      static const std::vector<std::string> result{"Performance Curve Output Value", "Performance Curve Input Variable 1 Value",
                                                   "Performance Curve Input Variable 2 Value"};
      return result;
    }

    IddObjectType CurveFanPressureRise_Impl::iddObjectType() const {
      return CurveFanPressureRise::iddObjectType();
    }

    int CurveFanPressureRise_Impl::numVariables() const {
      return 2;
    }

    double CurveFanPressureRise_Impl::evaluate(const std::vector<double>& independentVariables) const {
      OS_ASSERT(independentVariables.size() == 2u);

      double Qfan = independentVariables[0];
      if (Qfan < minimumValueofQfan()) {
        LOG(Warn, "Supplied Qfan is below the minimumValueofx, resetting it.");
        Qfan = minimumValueofQfan();
      }
      if (Qfan > maximumValueofQfan()) {
        LOG(Warn, "Supplied Qfan is above the maximumValueofx, resetting it.");
        Qfan = maximumValueofQfan();
      }

      double Psm = independentVariables[1];
      if (Psm < minimumValueofPsm()) {
        LOG(Warn, "Supplied Psm is below the minimumValueofPsm, resetting it.");
        Psm = minimumValueofPsm();
      }
      if (Psm > maximumValueofPsm()) {
        LOG(Warn, "Supplied Psm is above the maximumValueofPsm, resetting it.");
        Psm = maximumValueofPsm();
      }

      double result = coefficient1C1() * pow(Qfan, 2);
      result += coefficient2C2() * Qfan;
      result += coefficient3C3() * Qfan * sqrt(Psm);
      result += coefficient4C4() * Psm;

      if (boost::optional<double> _minVal = minimumCurveOutput()) {
        double minVal = _minVal.get();
        if (result < minVal) {
          LOG(Warn, "Calculated curve output is below minimumCurveOutput, resetting it.");
          result = minVal;
        }
      }

      if (boost::optional<double> _maxVal = maximumCurveOutput()) {
        double maxVal = _maxVal.get();
        if (result > maxVal) {
          LOG(Warn, "Calculated curve output is above maximumCurveOutput, resetting it.");
          result = maxVal;
        }
      }

      return result;
    }

    double CurveFanPressureRise_Impl::coefficient1C1() const {
      boost::optional<double> value = getDouble(OS_Curve_FanPressureRiseFields::Coefficient1C1, true);
      OS_ASSERT(value);
      return value.get();
    }

    double CurveFanPressureRise_Impl::coefficient2C2() const {
      boost::optional<double> value = getDouble(OS_Curve_FanPressureRiseFields::Coefficient2C2, true);
      OS_ASSERT(value);
      return value.get();
    }

    double CurveFanPressureRise_Impl::coefficient3C3() const {
      boost::optional<double> value = getDouble(OS_Curve_FanPressureRiseFields::Coefficient3C3, true);
      OS_ASSERT(value);
      return value.get();
    }

    double CurveFanPressureRise_Impl::coefficient4C4() const {
      boost::optional<double> value = getDouble(OS_Curve_FanPressureRiseFields::Coefficient4C4, true);
      OS_ASSERT(value);
      return value.get();
    }

    double CurveFanPressureRise_Impl::minimumValueofQfan() const {
      boost::optional<double> value = getDouble(OS_Curve_FanPressureRiseFields::MinimumValueofQfan, true);
      OS_ASSERT(value);
      return value.get();
    }

    double CurveFanPressureRise_Impl::maximumValueofQfan() const {
      boost::optional<double> value = getDouble(OS_Curve_FanPressureRiseFields::MaximumValueofQfan, true);
      OS_ASSERT(value);
      return value.get();
    }

    double CurveFanPressureRise_Impl::minimumValueofPsm() const {
      boost::optional<double> value = getDouble(OS_Curve_FanPressureRiseFields::MinimumValueofPsm, true);
      OS_ASSERT(value);
      return value.get();
    }

    double CurveFanPressureRise_Impl::maximumValueofPsm() const {
      boost::optional<double> value = getDouble(OS_Curve_FanPressureRiseFields::MaximumValueofPsm, true);
      OS_ASSERT(value);
      return value.get();
    }

    boost::optional<double> CurveFanPressureRise_Impl::minimumCurveOutput() const {
      return getDouble(OS_Curve_FanPressureRiseFields::MinimumCurveOutput, true);
    }

    boost::optional<double> CurveFanPressureRise_Impl::maximumCurveOutput() const {
      return getDouble(OS_Curve_FanPressureRiseFields::MaximumCurveOutput, true);
    }

    bool CurveFanPressureRise_Impl::setCoefficient1C1(double coefficient1C1) {
      bool result = setDouble(OS_Curve_FanPressureRiseFields::Coefficient1C1, coefficient1C1);
      OS_ASSERT(result);
      return result;
    }

    bool CurveFanPressureRise_Impl::setCoefficient2C2(double coefficient2C2) {
      bool result = setDouble(OS_Curve_FanPressureRiseFields::Coefficient2C2, coefficient2C2);
      OS_ASSERT(result);
      return result;
    }

    bool CurveFanPressureRise_Impl::setCoefficient3C3(double coefficient3C3) {
      bool result = setDouble(OS_Curve_FanPressureRiseFields::Coefficient3C3, coefficient3C3);
      OS_ASSERT(result);
      return result;
    }

    bool CurveFanPressureRise_Impl::setCoefficient4C4(double coefficient4C4) {
      bool result = setDouble(OS_Curve_FanPressureRiseFields::Coefficient4C4, coefficient4C4);
      OS_ASSERT(result);
      return result;
    }

    bool CurveFanPressureRise_Impl::setMinimumValueofQfan(double minimumValueofQfan) {
      bool result = setDouble(OS_Curve_FanPressureRiseFields::MinimumValueofQfan, minimumValueofQfan);
      OS_ASSERT(result);
      return result;
    }

    bool CurveFanPressureRise_Impl::setMaximumValueofQfan(double maximumValueofQfan) {
      bool result = setDouble(OS_Curve_FanPressureRiseFields::MaximumValueofQfan, maximumValueofQfan);
      OS_ASSERT(result);
      return result;
    }

    bool CurveFanPressureRise_Impl::setMinimumValueofPsm(double minimumValueofPsm) {
      bool result = setDouble(OS_Curve_FanPressureRiseFields::MinimumValueofPsm, minimumValueofPsm);
      OS_ASSERT(result);
      return result;
    }

    bool CurveFanPressureRise_Impl::setMaximumValueofPsm(double maximumValueofPsm) {
      bool result = setDouble(OS_Curve_FanPressureRiseFields::MaximumValueofPsm, maximumValueofPsm);
      OS_ASSERT(result);
      return result;
    }

    bool CurveFanPressureRise_Impl::setMinimumCurveOutput(boost::optional<double> minimumCurveOutput) {
      bool result = false;
      if (minimumCurveOutput) {
        result = setDouble(OS_Curve_FanPressureRiseFields::MinimumCurveOutput, minimumCurveOutput.get());
      } else {
        result = setString(OS_Curve_FanPressureRiseFields::MinimumCurveOutput, "");
      }
      OS_ASSERT(result);
      return result;
    }

    void CurveFanPressureRise_Impl::resetMinimumCurveOutput() {
      bool result = setString(OS_Curve_FanPressureRiseFields::MinimumCurveOutput, "");
      OS_ASSERT(result);
    }

    bool CurveFanPressureRise_Impl::setMaximumCurveOutput(boost::optional<double> maximumCurveOutput) {
      bool result = false;
      if (maximumCurveOutput) {
        result = setDouble(OS_Curve_FanPressureRiseFields::MaximumCurveOutput, maximumCurveOutput.get());
      } else {
        result = setString(OS_Curve_FanPressureRiseFields::MaximumCurveOutput, "");
      }
      OS_ASSERT(result);
      return result;
    }

    void CurveFanPressureRise_Impl::resetMaximumCurveOutput() {
      bool result = setString(OS_Curve_FanPressureRiseFields::MaximumCurveOutput, "");
      OS_ASSERT(result);
    }

  }  // namespace detail

  CurveFanPressureRise::CurveFanPressureRise(const Model& model) : Curve(CurveFanPressureRise::iddObjectType(), model) {
    OS_ASSERT(getImpl<detail::CurveFanPressureRise_Impl>());
    setDouble(OS_Curve_FanPressureRiseFields::Coefficient1C1, 1.0);
    setDouble(OS_Curve_FanPressureRiseFields::Coefficient2C2, 1.0);
    setDouble(OS_Curve_FanPressureRiseFields::Coefficient3C3, 1.0);
    setDouble(OS_Curve_FanPressureRiseFields::Coefficient4C4, 1.0);
    setDouble(OS_Curve_FanPressureRiseFields::MinimumValueofQfan, 0.0);
    setDouble(OS_Curve_FanPressureRiseFields::MaximumValueofQfan, 10.0);
    setDouble(OS_Curve_FanPressureRiseFields::MinimumValueofPsm, 0.0);
    setDouble(OS_Curve_FanPressureRiseFields::MaximumValueofPsm, 500.0);
  }

  IddObjectType CurveFanPressureRise::iddObjectType() {
    IddObjectType result(IddObjectType::OS_Curve_FanPressureRise);
    return result;
  }

  double CurveFanPressureRise::coefficient1C1() const {
    return getImpl<detail::CurveFanPressureRise_Impl>()->coefficient1C1();
  }

  double CurveFanPressureRise::coefficient2C2() const {
    return getImpl<detail::CurveFanPressureRise_Impl>()->coefficient2C2();
  }

  double CurveFanPressureRise::coefficient3C3() const {
    return getImpl<detail::CurveFanPressureRise_Impl>()->coefficient3C3();
  }

  double CurveFanPressureRise::coefficient4C4() const {
    return getImpl<detail::CurveFanPressureRise_Impl>()->coefficient4C4();
  }

  double CurveFanPressureRise::minimumValueofQfan() const {
    return getImpl<detail::CurveFanPressureRise_Impl>()->minimumValueofQfan();
  }

  double CurveFanPressureRise::maximumValueofQfan() const {
    return getImpl<detail::CurveFanPressureRise_Impl>()->maximumValueofQfan();
  }

  double CurveFanPressureRise::minimumValueofPsm() const {
    return getImpl<detail::CurveFanPressureRise_Impl>()->minimumValueofPsm();
  }

  double CurveFanPressureRise::maximumValueofPsm() const {
    return getImpl<detail::CurveFanPressureRise_Impl>()->maximumValueofPsm();
  }

  boost::optional<double> CurveFanPressureRise::minimumCurveOutput() const {
    return getImpl<detail::CurveFanPressureRise_Impl>()->minimumCurveOutput();
  }

  boost::optional<double> CurveFanPressureRise::maximumCurveOutput() const {
    return getImpl<detail::CurveFanPressureRise_Impl>()->maximumCurveOutput();
  }

  bool CurveFanPressureRise::setCoefficient1C1(double coefficient1C1) {
    return getImpl<detail::CurveFanPressureRise_Impl>()->setCoefficient1C1(coefficient1C1);
  }

  bool CurveFanPressureRise::setCoefficient2C2(double coefficient2C2) {
    return getImpl<detail::CurveFanPressureRise_Impl>()->setCoefficient2C2(coefficient2C2);
  }

  bool CurveFanPressureRise::setCoefficient3C3(double coefficient3C3) {
    return getImpl<detail::CurveFanPressureRise_Impl>()->setCoefficient3C3(coefficient3C3);
  }

  bool CurveFanPressureRise::setCoefficient4C4(double coefficient4C4) {
    return getImpl<detail::CurveFanPressureRise_Impl>()->setCoefficient4C4(coefficient4C4);
  }

  bool CurveFanPressureRise::setMinimumValueofQfan(double minimumValueofQfan) {
    return getImpl<detail::CurveFanPressureRise_Impl>()->setMinimumValueofQfan(minimumValueofQfan);
  }

  bool CurveFanPressureRise::setMaximumValueofQfan(double maximumValueofQfan) {
    return getImpl<detail::CurveFanPressureRise_Impl>()->setMaximumValueofQfan(maximumValueofQfan);
  }

  bool CurveFanPressureRise::setMinimumValueofPsm(double minimumValueofPsm) {
    return getImpl<detail::CurveFanPressureRise_Impl>()->setMinimumValueofPsm(minimumValueofPsm);
  }

  bool CurveFanPressureRise::setMaximumValueofPsm(double maximumValueofPsm) {
    return getImpl<detail::CurveFanPressureRise_Impl>()->setMaximumValueofPsm(maximumValueofPsm);
  }

  bool CurveFanPressureRise::setMinimumCurveOutput(double minimumCurveOutput) {
    return getImpl<detail::CurveFanPressureRise_Impl>()->setMinimumCurveOutput(minimumCurveOutput);
  }

  void CurveFanPressureRise::resetMinimumCurveOutput() {
    getImpl<detail::CurveFanPressureRise_Impl>()->resetMinimumCurveOutput();
  }

  bool CurveFanPressureRise::setMaximumCurveOutput(double maximumCurveOutput) {
    return getImpl<detail::CurveFanPressureRise_Impl>()->setMaximumCurveOutput(maximumCurveOutput);
  }

  void CurveFanPressureRise::resetMaximumCurveOutput() {
    getImpl<detail::CurveFanPressureRise_Impl>()->resetMaximumCurveOutput();
  }

  /// @cond
  CurveFanPressureRise::CurveFanPressureRise(std::shared_ptr<detail::CurveFanPressureRise_Impl> impl) : Curve(std::move(impl)) {}
  /// @endcond

}  // namespace model
}  // namespace openstudio
